# Purpose: Manages the Playwright browser instance lifecycle.
# Changes:
# - Removed attempts to access non-existent 'browser.process.pid'.
# - Added assert checks after launch/stop to help type checkers understand state.
# - Corrected ConfigError import path if necessary (already correct).

import logging
from playwright.async_api import (
    async_playwright,
    Playwright,
    Browser,
    Page,
    Error as PlaywrightError,
    BrowserContext  # Import BrowserContext for type hinting
)
from typing import Optional, Dict, Any

# Use relative imports for core components
from ...core.exceptions import SkillError, ConfigError
from ...core.config import get_config_value

# Use get_logger from core setup
from ...core.logging_setup import get_logger
logger = get_logger(__name__)

# --- Playwright Global State ---
_playwright_instance: Optional[Playwright] = None
_browser_instance: Optional[Browser] = None
_browser_context_instance: Optional[BrowserContext] = None # Store context separately
_page_instance: Optional[Page] = None

# --- Helper ---
def _get_web_config() -> Dict[str, Any]:
    """Safely retrieves the web_automation skill configuration."""
    config = get_config_value('skills.web_automation', {})
    if not isinstance(config, dict):
        logger.warning("Web automation skill config is not a dictionary, using defaults.")
        return {}
    if not config:
        logger.warning("Web automation skill config not found or empty, using defaults.")
        return {}
    return config

async def start_playwright(config: Optional[Dict[str, Any]] = None) -> None:
    """Starts Playwright, launches browser, creates context and initial page."""
    global _playwright_instance, _browser_instance, _browser_context_instance, _page_instance

    if _playwright_instance:
        logger.warning("Playwright already started. Skipping initialization.")
        return

    skill_config = config if config is not None else _get_web_config()
    if not skill_config.get('enabled', True):
        logger.info("Web automation skill disabled. Skipping Playwright start.")
        return
    if skill_config.get('engine', 'playwright') != 'playwright':
        logger.info("Web automation engine not 'playwright'. Skipping Playwright start.")
        return

    browser_type = skill_config.get("default_browser", "chromium").lower()
    headless = skill_config.get("headless", False)
    launch_options = {"headless": headless}
    proxy_config = skill_config.get("proxy")
    if proxy_config and isinstance(proxy_config, dict) and proxy_config.get("server"):
        launch_options["proxy"] = proxy_config
        logger.info(f"Using proxy configuration: {proxy_config.get('server')}")

    temp_pw = None # Temporary variable for cleanup
    try:
        logger.info("Starting Playwright...")
        temp_pw = await async_playwright().start()
        _playwright_instance = temp_pw # Assign to global only on success past this point
        logger.info("Playwright process started.")

        logger.info(f"Launching browser: type='{browser_type}', options={launch_options}")
        browser_launcher: Optional[Any] = None
        if browser_type == "chromium":    browser_launcher = _playwright_instance.chromium
        elif browser_type == "firefox":   browser_launcher = _playwright_instance.firefox
        elif browser_type == "webkit":    browser_launcher = _playwright_instance.webkit
        else:
            raise SkillError(skill_name="start_playwright", message=f"Unsupported browser type: {browser_type}")

        if not browser_launcher: # Should not happen with check above, but safeguard
             raise SkillError(skill_name="start_playwright", message="Browser launcher could not be determined.")

        _browser_instance = await browser_launcher.launch(**launch_options)
        # Check if browser launch actually succeeded
        if not _browser_instance or not _browser_instance.is_connected():
             raise SkillError(skill_name="start_playwright", message=f"Browser launch failed or disconnected immediately.")
        logger.info(f"{browser_type.capitalize()} browser launched successfully.")

        # Assertion helps type checker understand _browser_instance is not None here
        assert _browser_instance is not None

        # Create browser context with options
        context_options: Dict[str, Any] = {}
        viewport = skill_config.get("viewport")
        if viewport and isinstance(viewport, dict): context_options["viewport"] = viewport
        user_agent = skill_config.get("user_agent")
        if user_agent and isinstance(user_agent, str): context_options["user_agent"] = user_agent
        # Add other context options from config as needed...
        # context_options["ignore_https_errors"] = skill_config.get("ignore_https_errors", False)

        logger.info(f"Creating new browser context with options: {context_options}")
        # Use the non-optional _browser_instance here
        _browser_context_instance = await _browser_instance.new_context(**context_options)
        if not _browser_context_instance:
             raise SkillError(skill_name="start_playwright", message="Failed to create browser context.")

        _page_instance = await _browser_context_instance.new_page()
        if not _page_instance:
             raise SkillError(skill_name="start_playwright", message="Failed to create initial page.")
        logger.info("Initial browser context and page created.")

        # Set default timeouts
        default_timeout_sec = skill_config.get("default_timeout", 30)
        try:
            timeout_ms = int(default_timeout_sec * 1000)
            if timeout_ms <= 0: raise ValueError("Timeout must be positive")
            _page_instance.set_default_timeout(timeout_ms)
            _page_instance.set_default_navigation_timeout(timeout_ms)
            logger.info(f"Default page timeout set to {default_timeout_sec} seconds.")
        except (TypeError, ValueError) as time_err:
            logger.error(f"Invalid default_timeout value '{default_timeout_sec}': {time_err}. Using Playwright default.")

    except Exception as e:
        logger.critical(f"Error during Playwright startup sequence: {e}", exc_info=True)
        # Attempt cleanup using potentially assigned instances or the temp one
        await stop_playwright() # stop_playwright handles None checks internally
        # Re-raise as SkillError
        original_exc = e if isinstance(e, PlaywrightError) else None
        raise SkillError(skill_name="start_playwright", message=f"Playwright startup failed: {e}", original_exception=original_exc) from e


async def stop_playwright() -> None:
    """Stops the browser, context, and the Playwright instance gracefully."""
    global _playwright_instance, _browser_instance, _browser_context_instance, _page_instance

    # Use temporary variables for safe handling during async closing
    page_to_close = _page_instance
    context_to_close = _browser_context_instance
    browser_to_close = _browser_instance
    playwright_to_stop = _playwright_instance

    # Clear global references immediately to prevent reuse attempts
    _page_instance = None
    _browser_context_instance = None
    _browser_instance = None
    _playwright_instance = None

    # Close in reverse order: Page -> Context -> Browser -> Playwright
    if page_to_close and not page_to_close.is_closed(): # Check if already closed
        logger.debug("Closing Playwright page...")
        try:
             await page_to_close.close()
        except PlaywrightError as e:
             logger.error(f"Error closing page: {e}", exc_info=False)

    if context_to_close:
        logger.debug("Closing Playwright browser context...")
        try:
            await context_to_close.close()
        except PlaywrightError as e:
             logger.error(f"Error closing browser context: {e}", exc_info=False)

    if browser_to_close and browser_to_close.is_connected(): # Check if connected
        logger.info("Closing browser instance...")
        try:
            await browser_to_close.close()
            logger.info("Browser instance closed successfully.")
        except PlaywrightError as e:
             logger.error(f"Playwright error closing browser: {e}", exc_info=False)
        except Exception as e: # Catch other potential errors during close
             logger.error(f"Unexpected error closing browser: {e}", exc_info=True)

    if playwright_to_stop:
        logger.info("Stopping Playwright process...")
        try:
            await playwright_to_stop.stop()
            logger.info("Playwright process stopped successfully.")
        except PlaywrightError as e:
             logger.error(f"Playwright error stopping instance: {e}", exc_info=False)
        except Exception as e:
             logger.error(f"Unexpected error stopping Playwright: {e}", exc_info=True)
    else:
        logger.debug("Playwright stop called, but instance was not running or already cleared.")


def get_current_page() -> Page:
    """Returns the currently managed Playwright Page object."""
    # Perform checks on global state
    if not _browser_instance or not _browser_instance.is_connected():
        logger.error("Attempted to get page, but browser is not active or disconnected.")
        raise SkillError(skill_name="get_current_page", message="Browser session is not active or disconnected.")
    # Check page specifically
    if not _page_instance or _page_instance.is_closed():
        logger.error("Attempted to get page, but the page is not initialized or is closed.")
        raise SkillError(skill_name="get_current_page", message="Browser page is not active or closed.")
    return _page_instance